package com.string.pokergame.server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 游戏的服务器端,用于接收客户端的信息和发送控制游戏的游戏状态
 *
 * @author String
 */
public class GameServer {
    ServerSocket serverSocket = null;
    ExecutorService execut = Executors.newFixedThreadPool(50);
    Room[] room = new Room[10];
    List<String> nameList = new ArrayList<>();
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public GameServer() {
        try {
            // 服务器接收端口为3000
            serverSocket = new ServerSocket(3000);
            // 初始化每个房间
            for (int i = 0; i < room.length; i++) {
                room[i] = new Room();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        Socket clientSockt = null;
        while (true) {
            try {
                System.out.println(sdf.format(new Date()) + " " + "本机IP地址为:" + ServerUtil.getLocalInetAddress());
                System.out.println(sdf.format(new Date()) + " " + "本机端口号为:3000");
                System.out.println(sdf.format(new Date()) + " " + "等待客户端连接...");
                clientSockt = serverSocket.accept();
                System.out.println(sdf.format(new Date()) + " " + "一台客户端连接成功!");

                ClientHandler ch = new ClientHandler(clientSockt);
                execut.execute(ch);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        GameServer server = new GameServer();
        server.start();
    }

    private class ClientHandler implements Runnable {
        private Socket clientSocket = null;

        private volatile boolean exit = false;

        public ClientHandler(Socket socket) {
            clientSocket = socket;
        }

        public void exit() {
            this.exit = true;
        }

        private void stopSocket() {
            try {
                if (clientSocket != null) {
                    clientSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            InputStream clientIs = null;
            InputStreamReader clientIsr = null;
            BufferedReader clientBr = null;
            OutputStream clientOs = null;
            OutputStreamWriter clientOsw = null;
            PrintWriter clientPw = null;
            String name = null, joinOrNot = null, ready = null, tmpMsg = null;
            int roomNum = -1, playerID = -1, toDoId = -1;
            try {
                clientIs = clientSocket.getInputStream();
                clientIsr = new InputStreamReader(clientIs, StandardCharsets.UTF_8);
                clientBr = new BufferedReader(clientIsr);
                clientOs = clientSocket.getOutputStream();
                clientOsw = new OutputStreamWriter(clientOs, StandardCharsets.UTF_8);
                clientPw = new PrintWriter(clientOsw, true);

                name = clientBr.readLine();
                // 判断名字是否合法或者重复,传回客户端对应的信息
                while (name == null || nameList.contains(name) || "".equals(name)) {
                    clientPw.println("N");
                    name = clientBr.readLine();
                }
                clientPw.println("Y");
                nameList.add(name);
                // 该循环为进入大厅后的循环
                while (!exit) {
                    // 判断是加入房间还是创建新房间 该循环为大厅循环
                    while (!exit) {
                        joinOrNot = null;
                        while (!("Y".equals(joinOrNot) || "N".equals(joinOrNot))) {
                            joinOrNot = clientBr.readLine();
                        }
                        if ("Y".equals(joinOrNot)) {
                            tmpMsg = clientBr.readLine();
                            if (tmpMsg == null || "".equals(tmpMsg)) {
                                clientPw.println("ERROR");
                                continue;
                            }
                            roomNum = Integer.parseInt(tmpMsg) - 1;
                            if (room[roomNum].getpNum() == 0 || room[roomNum].getpNum() > 2) {
                                clientPw.println("ERROR");
                                continue;
                            }
                            clientPw.println("RIGHT");
                            playerID = room[roomNum].addPlayer(name, clientBr, clientPw);
                            System.out.println(sdf.format(new Date()) + " " + name + " 加入了第 " + (roomNum + 1) + " 桌游戏");
                            break;
                        } else {
                            roomNum = 0;
                            while (room[roomNum].getpNum() != 0) {
                                roomNum++;
                            }
                            clientPw.println(roomNum);
                            playerID = room[roomNum].addPlayer(name, clientBr, clientPw);
                            System.out.println(sdf.format(new Date()) + " " + name + " 加入了第 " + (roomNum + 1) + " 桌游戏");
                            break;
                        }
                    }
                    // 进入等待状态,如果接收到准备后则无法取消准备状态 该循环为房间循环,从准备到出房间
                    while (!exit) {
                        // 准备阶段的循环
                        boolean isReady = false;
                        while (!isReady) {
                            ready = clientBr.readLine();
                            // 如果用户在此时退出则退出准备循环
                            if ("EXIT".equals(ready)) {
                                room[roomNum].playerLeave(playerID);
                                playerID = -1;
                                clientPw.println("EXIT");
                                System.out.println(sdf.format(new Date()) + " " + name + " 退出了" + "第 " + (roomNum + 1) + " 桌");
                                break;
                            }
                            System.out.println(sdf.format(new Date()) + " " + "第 " + (roomNum + 1) + " 桌的 " + name + " 准备了!");

                            room[roomNum].ready(playerID);
                            isReady = true;
                            // 实时监听当前房间的准备人数和当前玩家的准备状态
                            while (room[roomNum].getReady() < 3) {
                                clientPw.println("ALIVE");
                                Thread.sleep(100);
                                // 如果发现客户端取消准备则退出准备状态监听循环
                                if ("NOREADY".equals(clientBr.readLine())) {
                                    isReady = false;
                                    room[roomNum].cancelReady(playerID);
                                    System.out.println(sdf.format(new Date()) + " "
                                            + "第 " + (roomNum + 1) + " 桌的 " + name + " 取消准备了!");
                                    break;
                                }
                            }
                        }
                        // 全部已准备,开始游戏 该循环为游戏循环,从发牌到游戏结束
                        if (playerID != -1) {
                            clientPw.println("START"); // 发送开始信号
                            while (!exit) {
                                // 发牌
                                clientPw.println("GIVECARDS"); // 发送发牌信号,客户端开始接收卡牌
                                for (int i = 0; i < 17; i++) {
                                    room[roomNum].giveCard(playerID);
                                    try {
                                        Thread.sleep(200);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                // 等待三个玩家接受完全部的手牌
                                clientBr.readLine();
                                room[roomNum].over();
                                while (room[roomNum].getOver() < 3) {
                                    Thread.sleep(200);
                                }
                                // 发送三张地主牌
                                room[roomNum].giveLordCards(playerID);
                                // 等待三位玩家接受完全部的地主牌并且显示完毕后再开始抢地主环节
                                clientBr.readLine();
                                room[roomNum].over();
                                while (room[roomNum].getOver() < 6) {
                                    Thread.sleep(200);
                                }
                                // 将下一轮的牌洗好
                                room[roomNum].newTotal();
                                // 随机一个人开始叫地主
                                synchronized (room[roomNum]) {
                                    if (room[roomNum].getRobLord() == 0) {// 判断其中是否有线程已进入
                                        room[roomNum].setRobLord(room[roomNum].getRobLord() + 1);
                                        toDoId = (int) (Math.random() * 3);
                                        toDoId = room[roomNum].robLord(toDoId);
                                        if (toDoId == -1) {
                                            room[roomNum].setReady(0);
                                            room[roomNum].setPutCards(false);
                                            room[roomNum].setOver(0);
                                            room[roomNum].setRobLord(-1);
                                            continue;
                                        }
                                        room[roomNum].putCards(toDoId);
                                        break;
                                    }
                                }
                                // 未进入的线程等待游戏结束
                                while (room[roomNum].isPutCards()) {
                                    Thread.sleep(500);
                                }
                                if (room[roomNum].getRobLord() == -1) {
                                    room[roomNum].setOver(0);
                                    room[roomNum].setReady(0);
                                    Thread.sleep(550);
                                    room[roomNum].setRobLord(0);
                                    room[roomNum].setPutCards(true);
                                    continue;
                                } else {
                                    break;
                                }
                            }
                        }
                        // 游戏结束,判断是继续还是退出房间,如果playerID为-1则直接退出房间循环
                        if (playerID == -1) {
                            break;
                        } else {
                            Thread.sleep(600);
                            room[roomNum].setOver(0);
                            room[roomNum].setReady(0);
                            room[roomNum].setRobLord(0);
                            room[roomNum].setPutCards(true);
                            System.out.println(sdf.format(new Date()) + " " + "第 " + roomNum + " 桌的游戏结束了!");
                            tmpMsg = clientBr.readLine();
                            if ("CONTINUE".equals(tmpMsg)) {
                                System.out.println(sdf.format(new Date()) + " " + playerID + " 继续游戏!");
                                continue;
                            } else {
                                clientPw.println("EXIT");
                                room[roomNum].playerLeave(playerID);
                                System.out.println(sdf.format(new Date()) + " " + name + " 退出了" + "第 " + roomNum + " 桌");
                                break;
                            }
                        }
                    }
                }
            } catch (SocketException ignore) {
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                nameList.remove(name);
                if (roomNum != -1
                        && playerID != -1) {
                    room[roomNum].playerLeave(playerID);
                }
                if (roomNum != -1
                        && room[roomNum].clear()) {
                    room[roomNum] = new Room();
                }
                stopSocket();
                System.out.println(sdf.format(new Date()) + " " + "客户端 " + name + " 下线了!");
            }
        }
    }
}
